Repository navigation
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🚀 Deploying Preview to Cloudflare 🚀Preview Deployments by commit
View all previews: View all previews ↗ |
|
Contributor
|
| File | Main | PR | Change |
|---|---|---|---|
addons.html |
354.63 KB | 361.27 KB | +6.64 KB (+1.9%) |
embedding.html |
59.79 KB | 61.38 KB | +1.59 KB (+2.7%) |
Performance estimate (single CI run)
- Generation time: 56.2% faster (39.43 s → 17.27 s)
- Peak memory: 28.5% lower (2.48 GB → 1.77 GB)
legacy-json Generator
Performance estimate (single CI run)
- Generation time: 7.5% slower (8.36 s → 8.99 s)
- Peak memory: 21.6% lower (1.57 GB → 1.23 GB)
llms-txt Generator
Performance estimate (single CI run)
- Generation time: 64.3% slower (4.87 s → 8.00 s)
- Peak memory: 32.3% lower (1.77 GB → 1.20 GB)
orama-db Generator
Output size: 1 file changed · net +1.00 B
File size details
| File | Main | PR | Change |
|---|---|---|---|
orama-db.json |
9.57 MB | 9.57 MB | +1.00 B (+0.0%) |
Performance estimate (single CI run)
- Generation time: 55.1% slower (5.52 s → 8.56 s)
- Peak memory: 31.0% lower (1.84 GB → 1.27 GB)
web Generator
Output size: 68 files changed · net -1.60 KB
File size details
| File | Main | PR | Change |
|---|---|---|---|
assets/SearchBox-wQQHMZuJ.js |
84.67 KB | — | -84.67 KB (-100.0%) |
assets/SearchBox-DjNnuz7W.js |
— | 84.67 KB | +84.67 KB |
assets/dist-D5dOZyAD.js |
30.42 KB | — | -30.42 KB (-100.0%) |
assets/dist-DruvUqXN.js |
— | 30.42 KB | +30.42 KB |
assets/SideBar-CjBhW9Hu.js |
25.62 KB | — | -25.62 KB (-100.0%) |
assets/SideBar-DaKBoTPQ.js |
— | 25.62 KB | +25.62 KB |
assets/client-eAdjoyYV.js |
24.91 KB | — | -24.91 KB (-100.0%) |
assets/client-DxC3O6Ca.js |
— | 24.91 KB | +24.91 KB |
assets/config-Cx-IX3nR.js |
24.37 KB | — | -24.37 KB (-100.0%) |
assets/config-DtT2jlD0.js |
— | 24.09 KB | +24.09 KB |
assets/Combination-0Ghc6FKv.js |
16.11 KB | — | -16.11 KB (-100.0%) |
assets/Combination-d5oRXla-.js |
— | 16.11 KB | +16.11 KB |
assets/ThemeToggle-BZ0_KYDS.js |
13.65 KB | — | -13.65 KB (-100.0%) |
assets/ThemeToggle-DVZdvZ7l.js |
— | 13.65 KB | +13.65 KB |
assets/dist-5O-XzCME.js |
10.20 KB | — | -10.20 KB (-100.0%) |
assets/dist-DVEbYasF.js |
— | 10.20 KB | +10.20 KB |
assets/Layout-N2aBwWVg.js |
10.13 KB | — | -10.13 KB (-100.0%) |
assets/Layout-C5yPmBoc.js |
— | 10.13 KB | +10.13 KB |
assets/compat-DmJKK7tl.js |
10.13 KB | — | -10.13 KB (-100.0%) |
assets/compat-ByVaDh64.js |
— | 10.13 KB | +10.13 KB |
assets/Tooltip-_V0pbK8z.js |
7.93 KB | — | -7.93 KB (-100.0%) |
assets/Tooltip-C41LEB8U.js |
— | 7.93 KB | +7.93 KB |
assets/dist-CX7YxNak.js |
6.99 KB | — | -6.99 KB (-100.0%) |
assets/dist-DNbUuC3x.js |
— | 6.99 KB | +6.99 KB |
assets/jsx-runtime-BXRXL30K.js |
5.67 KB | — | -5.67 KB (-100.0%) |
assets/jsx-runtime-CW012A50.js |
— | 5.67 KB | +5.67 KB |
assets/dist-D7-8NdKu.js |
3.93 KB | — | -3.93 KB (-100.0%) |
assets/dist-cDFsq_2f.js |
— | 3.93 KB | +3.93 KB |
assets/CodeTabs-BCN1ZuaS.js |
3.89 KB | — | -3.89 KB (-100.0%) |
assets/CodeTabs-DT17uorw.js |
— | 3.89 KB | +3.89 KB |
assets/CodeBox-BU9A6SQ0.js |
3.44 KB | — | -3.44 KB (-100.0%) |
assets/CodeBox-BNILFdqT.js |
— | 3.44 KB | +3.44 KB |
assets/hooks.module-DmVOR8gj.js |
3.40 KB | — | -3.40 KB (-100.0%) |
assets/hooks.module-BbOQEtt2.js |
— | 3.40 KB | +3.40 KB |
assets/FunctionSignature-BkGQ3GtN.js |
2.28 KB | — | -2.28 KB (-100.0%) |
assets/FunctionSignature-BQoQjJ6P.js |
— | 2.28 KB | +2.28 KB |
assets/Banner-DmiHcrIG.js |
2.13 KB | — | -2.13 KB (-100.0%) |
assets/Banner-Zf4myBWm.js |
— | 2.13 KB | +2.13 KB |
assets/ChangeHistory-Py08-aa6.js |
1.77 KB | — | -1.77 KB (-100.0%) |
assets/ChangeHistory-C3DUcRCX.js |
— | 1.77 KB | +1.77 KB |
assets/DataTag-8MiOm8lc.js |
856.00 B | — | -856.00 B (-100.0%) |
assets/DataTag-CTUSGgLj.js |
— | 856.00 B | +856.00 B |
assets/DocumentationIndex-C41jjOOT.js |
833.00 B | — | -833.00 B (-100.0%) |
assets/DocumentationIndex-D_jFywVL.js |
— | 833.00 B | +833.00 B |
addons.html |
377.82 KB | 377.09 KB | -751.00 B (-0.2%) |
all.html |
33.10 MB | 33.10 MB | -679.00 B (-0.0%) |
assets/ArrowUpRightIcon-CeI6pEnL.js |
618.00 B | — | -618.00 B (-100.0%) |
assets/ArrowUpRightIcon-BSpTwAaB.js |
— | 618.00 B | +618.00 B |
assets/Badge-Cd_pO_Vx.js |
616.00 B | — | -616.00 B (-100.0%) |
assets/Badge-CZF-RiB7.js |
— | 616.00 B | +616.00 B |
assets/AlertBox-rhpuVHCX.js |
591.00 B | — | -591.00 B (-100.0%) |
assets/AlertBox-BNnsWt2b.js |
— | 591.00 B | +591.00 B |
assets/CodeBracketIcon-NvhDSqX7.js |
512.00 B | — | -512.00 B (-100.0%) |
assets/CodeBracketIcon-TvXI-m3T.js |
— | 512.00 B | +512.00 B |
assets/dist-BDAKpzg5.js |
477.00 B | — | -477.00 B (-100.0%) |
assets/dist-D1kGHM6c.js |
— | 477.00 B | +477.00 B |
assets/ChevronDownIcon-CTgBO3tH.js |
468.00 B | — | -468.00 B (-100.0%) |
assets/ChevronDownIcon-BOnMJQ3A.js |
— | 468.00 B | +468.00 B |
assets/renderLabel-_7gv3mHW.js |
447.00 B | — | -447.00 B (-100.0%) |
assets/renderLabel-CdEZo9LC.js |
— | 447.00 B | +447.00 B |
assets/Blockquote-D4eEaruL.js |
167.00 B | — | -167.00 B (-100.0%) |
assets/Blockquote-C7yOM0SI.js |
— | 167.00 B | +167.00 B |
assets/useRemoteConfig-BPIYr4xA.js |
151.00 B | — | -151.00 B (-100.0%) |
assets/useRemoteConfig-DRPTLZY3.js |
— | 151.00 B | +151.00 B |
n-api.html |
1.00 MB | 1.00 MB | +113.00 B (+0.0%) |
assets/withIsland-BKNTtLQ7.js |
105.00 B | — | -105.00 B (-100.0%) |
assets/withIsland-C7ctRfG5.js |
— | 105.00 B | +105.00 B |
embedding.html |
69.31 KB | 69.27 KB | -41.00 B (-0.1%) |
Performance estimate (single CI run)
- Generation time: 351.8% slower (46.98 s → 212.24 s)
- Peak memory: 38.3% lower (3.72 GB → 2.30 GB)
avivkeller
reviewed
Oct 7, 2026
ovflowd
commented
Oct 8, 2026
ovflowd
marked this pull request as ready for review
October 8, 2026 15:35
ovflowd
marked this pull request as draft
October 8, 2026 15:41
Member
Author
|
Marking as draft because #1157 should be merged first. |
4 tasks done
ovflowd
force-pushed
the
perf/memory-usage
branch
from
October 10, 2026 11:59
4f2e846 to
dd8f6c1
Compare
ovflowd
force-pushed
the
perf/memory-usage
branch
from
October 10, 2026 14:29
dd8f6c1 to
6c45a67
Compare
ovflowd
force-pushed
the
perf/memory-usage
branch
from
October 10, 2026 15:19
6c45a67 to
ca2087e
Compare
ovflowd
marked this pull request as ready for review
October 10, 2026 15:20
ovflowd
force-pushed
the
perf/memory-usage
branch
from
October 10, 2026 15:23
ca2087e to
b191224
Compare
avivkeller
reviewed
Oct 11, 2026
avivkeller
reviewed
Oct 11, 2026
avivkeller
reviewed
Oct 11, 2026
ovflowd
force-pushed
the
perf/memory-usage
branch
from
October 11, 2026 22:28
d8a7c0d to
fa3c3e4
Compare
ovflowd
force-pushed
the
perf/memory-usage
branch
from
October 11, 2026 22:48
fa3c3e4 to
b1b2556
Compare
ovflowd
marked this pull request as draft
October 11, 2026 22:48
ovflowd
force-pushed
the
perf/memory-usage
branch
from
October 12, 2026 00:57
b1b2556 to
7c7dfd7
Compare
ovflowd
force-pushed
the
perf/memory-usage
branch
from
October 12, 2026 02:07
7c7dfd7 to
f724336
Compare
The Shiki plugin registered every bundled language (~250 grammars) in each thread that highlighted code, which cost ~2s and ~100MB per thread and made every highlight several times slower, as each code block was matched against grammars it never uses. Importing it also imported all of them, through `@node-core/rehype-shiki`'s `LANGS` and its plugin, on every thread loading `jsx-ast`, the main thread included. The highlighter now registers a bundled language the first time code in it is highlighted, along with the bundled languages a configured one embeds, and lists the bundled ones from their metadata alone, without importing `LANGS`. The themes are given to Shiki by name, which it keeps parsed instead of parsing them for every highlight, named after the configured themes, as Shiki loads a theme given for both schemes once. A bundled language is registered with the bundled languages it embeds, lazily too (a fenced code block in Markdown): Shiki only highlights those once they are registered, so registering them later would make code highlight differently depending on what was highlighted before it. For the same reason, a bundled grammar injecting into others (`ts-tags`, into JavaScript's tagged templates) only injects into the language it was imported for. `@node-core/rehype-shiki` is updated to 1.5.1, whose plugin only imports every grammar when it isn't given a highlighter (nodejs/nodejs.org#9212). The grammars now come from doc-kit's own `shiki` dependency (4.4.3), which 1.5.1 shares, rather than the copy 1.5.0 pinned (4.3.1). Its C++ grammar highlights types and template arguments differently, which shows on the Node.js docs' C++ examples. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Aviv Keller <me@aviv.sh>
Every highlighted token of every code block, signature and type became a hast element, then a JSX element, then generated code, only to be rendered back into the same markup: most of what `jsx-ast` allocated, and much of what the pages' code took to compile and render. Highlighted code is static, as islands adopt it without rendering it again, so it now reaches the pages as HTML, written by `hast-util-to-html` and held by a `<code>` through `dangerouslySetInnerHTML`. Code blocks are embedded by a plugin running after Shiki. Types and signatures are embedded as they are highlighted, before `rehype-raw`, which would otherwise parse each of their tokens again. It is the HTML Preact rendered, spelled differently in places: `hast-util-to-html` writes `<` as `<`, leaves `"` unescaped in text, and keeps Shiki's token styles without a trailing `;`. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
The pool renders a single task on the calling thread, so `all.html`, rendered on its own after the other pages, was rendered on the main thread. That held the whole site in its heap, and kept the pool from shutting its idle workers down until it was done. It is now rendered alongside the other pages, first, as it takes by far the longest. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
The main thread loads every generator of a run, but only ever calls `generate`; building the pages is the workers' job. Still, the generators imported what only their workers use, so the main thread loaded the TypeScript parser (WASM) and what building a page takes. Those are now imported where they are used. `getFullName` also moves to a module of its own: `buildBarProps`, which `section-pages` imports, and `buildContent` only need a page's full name, not the code that builds and highlights signatures. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
The threads rendering pages load the `html` generator's module, and so everything `generate` imports, along with `processing.mjs`. That included `config.mjs`, which loads Shiki for the languages' display names and a Markdown processor of its own: ~14MB and ~75ms per worker, for what only the main thread uses, while the workers render pages, which is when the build peaks. `createVirtualImports` moves into `config.mjs`, which `generate` imports when it bundles the site, so the main thread alone loads it. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
A worker that highlights code no longer registers every grammar Shiki bundles, only those of the code it highlighted. The comment now says what holds for every generator: each worker has a heap of its own, with the libraries and the pages it is working on. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
V8 lets a heap grow to several times its live data before collecting it, the more the higher its limit: four times from 2GB up, and on a machine with plenty of memory every worker's limit is 4GB, so each held several times what it was using. Each worker's old space is now limited to `WORKER_HEAP_SIZE` (512MB). The biggest page of the Node.js docs, `all.html`, takes ~300MB. Node's `--max-old-space-size` replaces the limit, to raise it, and it's what limits the main thread, which runs the generators with one thread. On Node core's build with 4 threads, the peak goes from 2.02GB to 1.67GB on Node 26, and from 2.44GB to 2.19GB on Node 24, for 3-5% more time spent collecting garbage. The build passes with workers limited to as little as ~350MB. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Aviv Keller <me@aviv.sh>
A worker only ended a second after its last task, so the `jsx-ast` workers and their heaps (~800MB on the Node.js docs) were still there while the main thread bundled the site, which is when the build peaked. They now end a quarter of a second after running out of work, soon enough to be gone while the site is bundled. On Node core's build with 4 threads on Node 26, the peak goes from ~2.31GB to ~1.91GB. Workers also end in some of the gaps between generators, so the build starts 10 or 11 workers rather than 8, which costs ~0.3s. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
Of the ~20,000 types the Node.js docs highlight, 741 are different:
`{string}` alone is on most pages. Each thread now keeps the markup of
the types it highlighted, by highlighter, so a type it has seen is only
embedded again, saving ~4% of the build's time and ~5% of its CPU.
Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
The `html` generator held the code of every page, the JSX program `jsx-ast` gave it (~100MB for Node core's docs), until the last page was written, though the pages are rendered from their compiled programs. Each page's code is now let go once its program is compiled; `html` is what reads `jsx-ast`'s output. That lowers the memory held while the pages render, not the build's peak, which comes as the site is bundled. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
Every thread loads the logger, and with it each transport: the GitHub one imported `@actions/core`, and through it `undici`, for the four functions printing workflow commands. That was 237 modules, 19MB and ~80ms on every thread, whichever transport is used. The transport now writes the commands itself, escaped as `@actions/core` does, for the same output: 10 modules, 3MB and 7ms. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
`generate` bundled the site and compiled the pages' programs in its own scope, so everything it set up for that, the bundler, the program builder and the compile function, stayed reachable while the pages rendered. With one thread, the main thread renders them all, and held it all the while. The bundling and the compiling move to `buildPrograms`, which returns the pages' tasks for the pool, so all of it is let go once it returns. On Node core's build with one thread on Node 26, the peak goes from ~2.66GB to ~2.45GB (medians of 8 runs). With four threads, where the workers render the pages, it doesn't change. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
It will be removed soon. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
The HTML generators minified every page with SWC's HTML minifier, compiled to WASM. Its memory grows to about twelve times the biggest page it is given, and is never returned: each thread rendering pages kept it until it ended, and on Node.js 24, the worker that minified the Node.js docs' 33MB `all.html` collected garbage on nearly every allocation from then on, as V8 found that memory too large for its heap limit. Minifying also took a fifth of the build's CPU. For that, the Node.js docs' pages were 5.9% smaller, but only 3.2% once gzipped and 2.1% with brotli, which is how hosts serve them: Cloudflare, Vercel, Netlify and GitHub Pages compress HTML by default. The pages are now written as rendered, and `@swc/html-wasm` is gone, along with `@doc-kit/core/utils/html-minifier.mjs`. `minify` still minifies scripts, styles and JSON, and the publishing docs now explain how to serve the pages compressed. On Node core's build with 4 threads on Node.js 26, the build takes 7.7s instead of 10.5s and peaks at ~1.68GB instead of ~2.12GB; with one thread, 15.2s instead of 22.5s. On Node.js 24, 7.9s instead of 51.7s. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
Assisted-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Aviv Keller <me@aviv.sh>
ovflowd
force-pushed
the
perf/memory-usage
branch
from
October 12, 2026 02:35
f724336 to
df529ef
Compare
This branch had an error being deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Building the Node.js docs peaks at 3.6–3.8GB of memory, while a single page only needs ~100MB. Node core only builds the HTML docs on machines with more than 5GiB of memory, and silently skips them otherwise. That's what happens on its
ubuntu-slimdoc CI, whose docs artifact has no HTML at all.This PR brings the peak down to 1.7–2.1GB, and the build time to about a third:
jsx-ast. The docs use about ten languages. Each one is now registered the first time code in it is highlighted, with the languages it embeds, which takes highlighting from ~17s of CPU to ~4s, and the themes go to Shiki by name, so it stops parsing them again for every block. Code highlights the same whatever a thread highlighted before: fences in a Markdown block are registered with it, and a grammar injecting into others (ts-tags) only does so in its own blocks.@node-core/rehype-shikiis updated to 1.5.1, whose plugin only imports every grammar when it isn't given a highlighter (nodejs/nodejs.org#9212).hast-util-to-html, and types and signatures are embedded as they're highlighted, beforerehype-rawwould parse each of their tokens again. Each thread also highlights a type only once (of the ~20,000 types the docs highlight, 741 differ).all.htmlrenders in the worker pool. It was rendered and minified on the main thread, after every other page, holding the whole site there (~1GB).all.html) and is never returned, so every thread rendering pages kept it until it ended, and minifying took a fifth of the build's CPU. On Node 24, the worker holding that memory collected garbage on nearly every allocation, which took the build from ~8s to ~52s. In exchange, the 741 pages were 5.9% smaller on disk, but only 3.2% smaller gzipped and 2.1% with brotli, which is how they're served: Cloudflare, Vercel, Netlify and GitHub Pages compress HTML on their own, and so do doc-kit.nodejs.org and nodejs.org. The pages,legacy-html's included, are now written as rendered,@swc/html-wasmis gone, anddocs/publishing.mdshows how to serve them compressed.minifystill minifies scripts, styles and JSON.jsx-astworkers' heaps (~800MB) are gone while the main thread bundles the site, which is when the build peaked.--max-old-space-sizereplaces the limit, to raise it.@actions/coreandundicion every thread (237 modules, 19MB and ~80ms per thread, down to 10 modules, 3MB and 7ms).How a build runs
The main thread schedules the generators, holds their results and bundles the site with Vite. The rest of the work goes to a pool of worker threads, which end once idle:
With
threads: 1, the main thread runs the chunks itself.Validation
node --run test(746 tests) passes at every commit, andnode --run format:checkandnode --run lintpass.mainonce both are minified withmain's minifier and parsed as HTML (hast-util-to-htmlspells a few characters in code differently than Preact did, such as<for<), on thewebtarget (71 pages), on Node core's config (741) and onlegacy-html(69), except the C++ code blocks ofaddons,embeddingandn-api(andall.html, which holds them), whose grammar now comes from core's ownshiki(4.4.3) instead of the one@node-core/rehype-shiki1.5.0 pinned (4.3.1).--max-old-space-size, which limits every isolate, the Node core build passes at 384MB (not at 320MB), wheremainneeds 768MB.Benchmarks
main→ this branch, building Node core's docs with its config (legacy-json-all+section-pages) unless noted. Medians of 3 interleaved runs on an M4 Pro; peak memory is the physical footprint of every process in the build.webtarget, Node 26With one thread, the main thread does all the work, and the workers' heap limit doesn't apply. Node's
--max-old-space-size=768brings that build to 1.47GB in about the same time.Related Issues
Refs: #815
Refs: nodejs/node#62045
Check List
node --run testand all tests passed.node --run format:check&node --run lint.